package br.com.lazyprogrammer.worldsoccermanager.network.task;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.http.AndroidHttpClient;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.ImageView;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import br.com.lazyprogrammer.worldsoccermanager.network.Request;
import br.com.lazyprogrammer.worldsoccermanager.network.interfaces.Downloader;
import br.com.lazyprogrammer.worldsoccermanager.util.BitmapCache;
import br.com.lazyprogrammer.worldsoccermanager.util.PoolData;

/**
 * This helper class download images from the Internet and binds those with the
 * provided ImageView.
 * 
 * <p>
 * It requires the INTERNET permission, which should be added to your
 * application's manifest file.
 * </p>
 * 
 * A local cache of downloaded images is maintained internally to improve
 * performance.
 */

public class ImageDownloaderTask implements Downloader {
	private static final String LOG_TAG = "ImageDownloader";

    private Request.Callback listener;
    private int position = 0;

    private Vector<BitmapDownloaderTask> tasksInProgress;
    private Vector<BitmapDownloaderTask> queue;

	public enum Mode {
		CORRECT
	}

	public ImageDownloaderTask() {
		this.tasksInProgress = new Vector<BitmapDownloaderTask>(5);
		this.queue = new Vector<BitmapDownloaderTask>();
	}

	/**
	 * Download the specified image from the Internet and binds it to the
	 * provided ImageView. The binding is immediate if the image is found in the
	 * cache and will be done asynchronously otherwise. A null bitmap will be
	 * associated to the ImageView if an error occurs.
	 * 
	 * @param url
	 *            The URL of the image to download.
	 * @param imageView
	 *            The ImageView to bind the downloaded image to.
	 */
    @Override
	public void download(String url, ImageView imageView) {
		download(url, imageView, null, 0);
	}


	public void download(String url, ImageView imageView, Request.Callback listener,
			int position) {
		this.listener = listener;
		this.position = position;
 
		Bitmap bitmap = getBitmapFromCache(url);
		if (bitmap == null) {
			forceDownload(url, imageView);
		} else {
			imageView.setImageBitmap(bitmap);
			if (listener != null)
				listener.onSuccess(getClass(), position);
		}
	}

	private void forceDownload(String url, ImageView imageView) {
		if (url == null) {
			imageView.setImageDrawable(null);
			return;
		}
		BitmapDownloaderTask task = new BitmapDownloaderTask(url,imageView);
		if (checkQueue(task)) {
			tasksInProgress.add(task);
			task.executeOnExecutor(PoolData.POOL_EXECUTOR);
		}

	}

	private boolean checkQueue(BitmapDownloaderTask task) {
		if (tasksInProgress.size() >= 4) {
			if (!isUrlAlreadyInQueue(task)) {
				queue.add(task);
			}
			return false;
		} else {
			if (!isUrlAlreadyInProgress(task)) {
				return true;
			} else {
				if (!isUrlAlreadyInQueue(task)) {
					queue.add(task);
				}
			}
		}
		return false;
	}
	
	private boolean isUrlAlreadyInProgress(BitmapDownloaderTask task) {
		boolean urlAlreadyInProgress = false;
		for (BitmapDownloaderTask t : tasksInProgress) {
			if (t.url.equals(task.url)) {
				urlAlreadyInProgress = true;
				break;
			}
		}
		
		return urlAlreadyInProgress;
	}
	
	private boolean isUrlAlreadyInQueue(BitmapDownloaderTask task) {
		boolean urlAlreadyInQueue = false;
		for (BitmapDownloaderTask t : queue) {
			if (t.url.equals(task.url)) {
				t.addImageView(task.imagesViews.get(0));
				urlAlreadyInQueue = true;
				break;
			}
		}
		
		return urlAlreadyInQueue;
	}

	Bitmap downloadBitmap(String url) {
		// final int IO_BUFFER_SIZE = 4 * 1024;
		Log.i("Downloading", url);
		// AndroidHttpClient is not allowed to be used from the main thread
		final HttpClient client = new DefaultHttpClient();
		HttpGet getRequest = null;
		try {
			getRequest = new HttpGet(new URI(url));
			if (getRequest.getURI().getHost() == null) {
				if (url.contains("amazonaws")) {
					Pattern pattern = Pattern.compile("http://(.+.)(s\\d.amazonaws.com)");
					Matcher matcher = pattern.matcher(url);
					if (matcher.find()) {
						url = url.replaceAll(matcher.group(1), "");
						url = url.replaceAll(matcher.group(2), matcher.group(2) + "/" + matcher.group(1).substring(0, matcher.group(1).length() - 1));
						getRequest = new HttpGet(new URI(url));
						
					}
				}
				
			}
			HttpResponse response = client.execute(getRequest);
			final int statusCode = response.getStatusLine().getStatusCode();
			if (statusCode != HttpStatus.SC_OK) {
				Log.w("ImageDownloader", "Error " + statusCode
						+ " while retrieving bitmap from " + url);
				return null;
			}

			final HttpEntity entity = response.getEntity();
			if (entity != null) {
				InputStream inputStream = null;
				try {
					inputStream = entity.getContent();
					// return BitmapFactory.decodeStream(inputStream);
					// Bug on slow connections, fixed in future release.
					Bitmap image = BitmapFactory
							.decodeStream(new FlushedInputStream(inputStream));
					// if (image == null)
					// image = downloadBitmap(url);
					return image;
					// return BitmapFactory.decodeStream( new
					// FlushedInputStream( inputStream ) );
				} catch (Exception e) {
                    e.printStackTrace();
				} finally {
					if (inputStream != null) {
						inputStream.close();
					}
					entity.consumeContent();
				}
			}
		} catch (IOException e) {
			if (getRequest != null) {
				getRequest.abort();
				Log.w(LOG_TAG, "I/O error while retrieving bitmap from " + url, e);
			}
		} catch (IllegalStateException e) {
			if (getRequest != null) {
				getRequest.abort();
				Log.w(LOG_TAG, "Incorrect URL: " + url);
			}
		} catch (Exception e) {
			if (getRequest != null) {
				getRequest.abort();
				Log.w(LOG_TAG, "Error while retrieving bitmap from " + url, e);
			}
		} finally {
			if ((client instanceof AndroidHttpClient)) {
				((AndroidHttpClient) client).close();
			}
		}
		return null;
	}

	/*
	 * An InputStream that skips the exact number of bytes provided, unless it
	 * reaches EOF.
	 */
	static class FlushedInputStream extends FilterInputStream {
		public FlushedInputStream(InputStream inputStream) {
			super(inputStream);
		}

		@Override
		public long skip(long n) throws IOException {
			long totalBytesSkipped = 0L;
			while (totalBytesSkipped < n) {
				long bytesSkipped = in.skip(n - totalBytesSkipped);
				if (bytesSkipped == 0L) {
					int b = read();
					if (b < 0) {
						break; // we reached EOF
					} else {
						bytesSkipped = 1; // we read one byte
					}
				}
				totalBytesSkipped += bytesSkipped;
			}
			return totalBytesSkipped;
		}
	}

	/**
	 * The actual AsyncTask that will asynchronously download the image.
	 */
	class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> {
		private String url;
		private Vector<ImageView> imagesViews;
		private Bitmap bitmap;
		private Vector<Integer> currentPositions;

		public BitmapDownloaderTask(String url, ImageView imageView) {
			this.url = url;
			this.imagesViews = new Vector<ImageView>();
			this.imagesViews.add(imageView);
			if (imageView.getTag() != null) {
				try {
					currentPositions = new Vector<Integer>();
					currentPositions.add((Integer) imageView.getTag());
				} catch (Exception e) {
					currentPositions.add(-1);
				}
			} else {
				currentPositions = new Vector<Integer>();
				currentPositions.add(-1);
			}
		}

		/**
		 * Actual download method.
		 */
		@Override
		protected Bitmap doInBackground(String... params) {;
			if (bitmap != null) {
				return bitmap;
			}
			return downloadBitmap(url);
		}

		/**
		 * Once the image is downloaded, associates it to the imageView
		 */
		@Override
		protected void onPostExecute(Bitmap bitmap) {

			// Log.i( "TTV", bitmap.getHeight() + " : " + bitmap.getWidth() );

			if (isCancelled()) {
				bitmap = null;
			}

			addBitmapToCache(url, bitmap);
			
			int i = 0;
			for (ImageView imageView : imagesViews) {
				int currentPosition = currentPositions.get(i++);
				boolean allowSet = true;
				
				if (imageView.getTag() != null && currentPosition != -1) {
					int position = (Integer) imageView.getTag();
					if (position != currentPosition) {
						allowSet = false;
					}
				}
				
				if (allowSet) {
					imageView.setImageBitmap(bitmap);
				}
				
			}
			
			if (listener != null)
				listener.onSuccess(getClass(), position);
			
			tasksInProgress.remove(this);
			processQueue();
		}
		
		private void addImageView(ImageView imageView) {
			boolean exists = false;
			for (ImageView image : imagesViews) {
				if (image.equals(imageView)) {
					exists = true;
					break;
				}
			}
			
			if (!exists) {
				imagesViews.add(imageView);
				if (imageView.getTag() != null) {
					try {
						currentPositions.add((Integer) imageView.getTag());
					} catch (Exception e) {
						currentPositions.add(-1);
					}
				} else {
					currentPositions.add(-1);
				}
			}
		}

		
	}
	
	private void processQueue() {
		if (queue != null && queue.size() > 0) {
			BitmapDownloaderTask task = queue.remove(0);
			task.bitmap = getBitmapFromCache(task.url);
			tasksInProgress.add(task);
			task.executeOnExecutor(PoolData.POOL_EXECUTOR);
			
			Log.i("beats", "Proccessing Queue - Current Size:"+queue.size());
		}
	}

	/**
	 * Adds this bitmap to the cache.
	 * 
	 * @param bitmap
	 *            The newly downloaded bitmap.
	 */
	private void addBitmapToCache(String url, Bitmap bitmap) {
		if (bitmap != null) {
			BitmapCache.addBitmap(url, bitmap);
		}
	}

	/**
	 * @param url
	 *            The URL of the image that will be retrieved from the cache.
	 * @return The cached bitmap or null if it was not found.
	 */
	private Bitmap getBitmapFromCache(String url) {
		return BitmapCache.getBitmap(url);
	}

}